// Author; Gonzalo Luzardo M.
// Contact: gonchalox@gmail.com, gluzardo@espol.edu.ec

import java.awt.Color;
import java.util.ArrayList;

import processing.core.PApplet;
import processing.core.PImage;

public class MosaicCreator {
	
	private PApplet m_applet;
	private boolean m_with_sites;
	private boolean m_with_fill;
	private boolean m_with_strokes;
	
	//Constructor
	public MosaicCreator(PApplet applet, boolean withFill, boolean withSites,boolean withStrokes){
		m_applet = applet;
		m_with_sites = withSites;
		m_with_fill = withFill;
		m_with_strokes = withStrokes;
	}
	
	//Create mosaic using points over images
	public void createMosaic(DelaunayTriangulationCreator delaunay_creator){
		//Using all points
		System.out.println("\nCreating mosaic...");
		for (Point point : delaunay_creator.getDelaunay().getPoints()) {
			ArrayList<Point> centri = new ArrayList<Point>();
			
			Triangle start = point.getTriangles().iterator().next();
			Point guide = start.getNextInCCW(point);
			Triangle tmp = start;
			
			do{
				Point center = Point.circumcenter(tmp.toArray(new Point[0]));
				center.setY(-center.getY());
				centri.add(center);
				Point tmpPoint = tmp.getVertexButNot(point,guide);
				tmp = delaunay_creator.getDelaunay().neighborOpposite(guide, tmp);
				guide = tmpPoint;
			}while(start!=tmp);
			
			draw(centri.toArray(new Point[0]), m_with_fill ? getColor(centri.toArray(new Point[0]), point, delaunay_creator.getFacesImages()) : null, m_with_strokes);
			if (m_with_sites)
				draw(point);
		}		
		System.out.println("Mosaic created...");
		
		
		//Using all points
		System.out.println("\nCreating edges mosaic...");
		for (Point point : delaunay_creator.getDelaunay().getPoints()) {
			ArrayList<Point> centri = new ArrayList<Point>();
			
			Triangle start = point.getTriangles().iterator().next();
			Point guide = start.getNextInCCW(point);
			Triangle tmp = start;
			
			do{
				Point center = Point.circumcenter(tmp.toArray(new Point[0]));
				center.setY(-center.getY());
				centri.add(center);
				Point tmpPoint = tmp.getVertexButNot(point,guide);
				tmp = delaunay_creator.getDelaunay().neighborOpposite(guide, tmp);
				guide = tmpPoint;
			}while(start!=tmp);
			
			drawEdges(centri.toArray(new Point[0]));
			if (m_with_sites)
				draw(point);
		}		
		System.out.println("Mosaic edges created...");
		
	}

	//Draw edges over polygon
	public void drawEdges(Point[] polygon) {
		int[] x = new int[polygon.length];
		int[] y = new int[polygon.length];
		//float weight;
		m_applet.stroke(0);
		m_applet.noFill();
		m_applet.beginShape();
		for (int i = 0; i < polygon.length; i++) {
			x[i] = (int) polygon[i].coord(0);
			y[i] = (int) polygon[i].coord(1);
			//Random stroke
			//weight = (float) (Math.random()*4);
			m_applet.strokeWeight(3);
			m_applet.strokeCap(0);
			m_applet.strokeJoin(0);
			m_applet.vertex(x[i], y[i]);
		}
		m_applet.endShape();
	
	}
		
	
	@SuppressWarnings("unused")
	private int getColor(Point item, PImage image) {
		return new Color(image.get((int) item.coord(0), (int) item.coord(1)))
				.getRGB();
	}

	@SuppressWarnings("unused")
	private int getColor(Triangle triangle, Point site,PImage image) {
		int meanR = 0;
		int meanG = 0;
		int meanB = 0;
		for (int i = 0; i < triangle.size(); i++) {
			Color tmp = new Color(image.get((int) triangle.get(i).coord(0),
					(int) triangle.get(i).coord(1)));
			meanR += tmp.getRed();
			meanG += tmp.getGreen();
			meanB += tmp.getBlue();
		}
		Color tmp = new Color(meanR / triangle.size(), meanG / triangle.size(),
				meanB / triangle.size());
		Color siteColor = new Color(image.get((int) site.coord(0), (int) site
				.coord(1)));
		meanR = (int) (0.75 * siteColor.getRed() + 0.25 * tmp.getRed());
		meanG = (int) (0.75 * siteColor.getGreen() + 0.25 * tmp.getGreen());
		meanB = (int) (0.75 * siteColor.getBlue() + 0.25 * tmp.getBlue());
		return new Color(meanR, meanG, meanB).getRGB();
	}

	private int getColor(Point[] centri, Point site,PImage image) {
		int meanR = 0;
		int meanG = 0;
		int meanB = 0;
		for (int i = 0; i < centri.length; i++) {
			Color tmp = new Color(image.get((int) centri[i].coord(0),
					(int) centri[i].coord(1)));
			meanR += tmp.getRed();
			meanG += tmp.getGreen();
			meanB += tmp.getBlue();
		}
		Color tmp = new Color(meanR / centri.length, meanG / centri.length,
				meanB / centri.length);
		Color siteColor = new Color(image.get((int) site.coord(0), (int) site
				.coord(1)));
		meanR = (int) (0.25 * siteColor.getRed() + 0.75 * tmp.getRed());
		meanG = (int) (0.25 * siteColor.getGreen() + 0.75 * tmp.getGreen());
		meanB = (int) (0.25 * siteColor.getBlue() + 0.75 * tmp.getBlue());
		return new Color(meanR, meanG, meanB).getRGB();
	}

	@SuppressWarnings("unused")
	private int getColor(Point[] item, PImage image) {
		int mean = 0;
		for (int i = 0; i < item.length; i++) {
			mean += image.get((int) item[i].coord(0), (int) item[i].coord(1));
		}
		// mean = mean / item.length;
		return mean;
	}

	public void draw(Point point) {
		m_applet.stroke(Color.black.getRed());
		int x = (int) point.coord(0);
		int y = (int) point.coord(1);
		m_applet.point(x, y);
	}

	/**
	 * Draw a polygon.
	 * 
	 * @param polygon
	 *            an array of polygon vertices
	 * @param fillColor
	 *            null implies no fill
	 */
	public void draw(Point[] polygon, int fillColor, boolean stroke) {
		int[] x = new int[polygon.length];
		int[] y = new int[polygon.length];
		m_applet.fill(fillColor);
		if (!stroke) {
			m_applet.noStroke();
		}
		m_applet.beginShape();
		for (int i = 0; i < polygon.length; i++) {
			x[i] = (int) polygon[i].coord(0);
			y[i] = (int) polygon[i].coord(1);

			m_applet.vertex(x[i], y[i]);

		}
		m_applet.endShape();
	}
}
